//
//  NSString+Extensions.m
//  NXLib
//
//  Created by AK on 14-3-27.
//  Copyright (c) 2014年 AK. All rights reserved.
//

#import "NSString+NXCategory.h"
#import <CommonCrypto/CommonDigest.h>

#if TARGET_OS_IPHONE
#import <MobileCoreServices/MobileCoreServices.h>
#endif



#define ELLIPSIS @"..."

@interface NSString (UsefulStuffPrivate)

+(BOOL) stringIsPalindrome:(NSString *)aString position:(NSInteger)position;

@end

@implementation NSString (NXCategory)

+ (NSString *)stringToSha1:(NSString *)str
{
    const char * s = [str cStringUsingEncoding:NSUTF8StringEncoding];
    NSData * keyData = [NSData dataWithBytes:s length:strlen(s)];
    
    // This is the destination
    uint8_t digest[CC_SHA1_DIGEST_LENGTH] = {0};
    // This one function does an unkeyed SHA1 hash of your hash data
    CC_SHA1(keyData.bytes, (int)keyData.length, digest);
    // Now convert to NSData structure to make it usable again
    NSData *out = [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH];
    NSString * hash = [out description];
    NSCharacterSet * doNotWants = [NSCharacterSet characterSetWithCharactersInString:@"<> "];
    hash = [[hash componentsSeparatedByCharactersInSet:doNotWants] componentsJoinedByString:@""];
    return hash;
}

+ (NSString *) stringTruncatedToWidth:(CGFloat)width withString:(NSString *)string andFont:(UIFont *)font
{
    return [string truncateToWidth:width withFont:font];
}

+ (BOOL)stringIsPalindrome:(NSString *)aString
{
    return [NSString stringIsPalindrome:aString position:0];
}

+ (BOOL)stringIsPalindrome:(NSString *)aString position:(NSInteger)position
{
    NSString * _string = [NSString stringWithString:aString];
    NSInteger _position = position;
    
    if (! _string)
    {
        return NO;
    }
    
    NSInteger stringLength = [_string length];
    NSString *firstChar = [[_string substringToIndex:_position] substringToIndex:1];
    NSString *lastChar = [[_string substringToIndex:(stringLength - 1 - _position)] substringToIndex:1];
    
    if (_position > (stringLength / 2))
    {
        return YES;
    }
    
    if (![firstChar isEqualToString:lastChar])
    {
        return NO;
    }
    
    return [NSString stringIsPalindrome:_string position:(_position + 1)];
}

- (NSString *)MD5Hash
{
    // Create a C-style pointer to the UT8-encoded contents of the NSString
    const char * pointer = [self UTF8String];
    
    // Create a buffer array big enough to hold the digest
    unsigned char buffer[CC_MD5_DIGEST_LENGTH];
    
    // Create 16-byte MD5 hash value, store in buffer
    // See: CC_MD5(3cc) manpage on OS X & iOS.
    
    CC_MD5(pointer, (int)strlen(pointer), buffer);
    
    // Convert MD5 digest in buffer to an autoreleased NSString of hexidecimal
    // values.
    NSMutableString * result = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
    for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i += 1) {
        [result appendFormat:@"%02x", buffer[i]];
    }
    
    return [result copy];
}

- (NSString *)truncateToWidth:(CGFloat)width withFont:(UIFont *)font
{
//    // Obtain a mutable copy of this NSString.
//    NSMutableString *truncatedString = [self mutableCopy];
//    
//    NSDictionary * tdic = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName,nil];
//    
////    CGSize size  = [text boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin |NSStringDrawingUsesFontLeading attributes:tdic context:nil].size;
//    
//    // If this NSString is longer than the desired width, truncate.
//    if ([self sizeWithFont:font].width > width)
//    {
//        // Subtract an ellipsis' worth of width from the desired width to obtain the
//        // truncation width.
//        width -= [ELLIPSIS sizeWithFont:font].width;
//        
//        // While the string is longer than the truncation width, remove characters
//        // from the end of the string.
//        NSRange range = {truncatedString.length - 1, 1};
//        while ([truncatedString sizeWithFont:font].width > width)
//        {
//            [truncatedString deleteCharactersInRange:range];
//            range.location -= 1;
//        }
//        
//        // Once truncation is complete, append an ellipsis to the end of the string.
//        [truncatedString replaceCharactersInRange:range withString:ELLIPSIS];
//    }
//    
//    return [truncatedString copy];
    return nil;
}

+ (NSString *)stringByFormattingBytes:(long long)bytes
{
	NSArray *units = [NSArray arrayWithObjects:@"%1.0f Bytes", @"%1.1f KB", @"%1.1f MB", @"%1.1f GB", @"%1.1f TB", nil];
	
	long long value = bytes * 10;
	for (NSUInteger i=0; i<[units count]; i++)
	{
		if (i > 0)
		{
			value = value/1024;
		}
		if (value < 10000)
		{
			return [NSString stringWithFormat:[units objectAtIndex:i], value/10.0];
		}
	}
	
	return [NSString stringWithFormat:[units objectAtIndex:[units count]-1], value/10.0];
}

- (BOOL)isPalindrome
{
    return [NSString stringIsPalindrome:self];
}

- (NSString *)reverse
{
    NSMutableString *reversedString = [NSMutableString stringWithCapacity:[self length]];
    
    for (int i = ((int)[self length] - 1); i >= 0; i -= 1)
    {
        [reversedString appendString:[NSString stringWithFormat:@"%C", [self characterAtIndex:i]]];
    }
    
    return reversedString;
}

- (NSNumber *)stringToNSNumber
{
    NSNumberFormatter * tmpFormatter = [[NSNumberFormatter alloc] init];
    [tmpFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
    NSNumber * theNumber = [tmpFormatter numberFromString:self];
    return theNumber;
}

- (BOOL)isEmpty
{
    if ([self length] <= 0 || self == (id)[NSNull null] || self == nil)
    {
        return YES;
    }
    return NO;
}

- (BOOL)stringContainsSubString:(NSString *)subString
{
    NSRange aRange = [self rangeOfString:subString];
    if (aRange.location == NSNotFound)
    {
        return NO;
    }
    return YES;
}

- (NSString *)stringByReplacingStringsFromDictionary:(NSDictionary*)dict
{
    NSMutableString * string = [self mutableCopy];
    for (NSString * target in dict)
    {
        [string replaceOccurrencesOfString:target withString:[dict objectForKey:target] options:0 range:NSMakeRange(0, [string length])];
    }
    return string;
}

+ (NSString *)stringWithUUID
{
	CFUUIDRef uuidObj = CFUUIDCreate(nil);//create a new UUID
	
	//get the string representation of the UUID
	NSString *uuidString = ( NSString *)CFBridgingRelease(CFUUIDCreateString(nil, uuidObj));
	
	CFRelease(uuidObj);
	return uuidString;
}

- (NSString *)md5Checksum
{
	const char *cStr = [self UTF8String];
	unsigned char result [CC_MD5_DIGEST_LENGTH];
	CC_MD5( cStr, (CC_LONG)strlen(cStr), result );
	
	return [NSString stringWithFormat: @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
			result[0], result[1],
			result[2], result[3],
			result[4], result[5],
			result[6], result[7],
			result[8], result[9],
			result[10], result[11],
			result[12], result[13],
			result[14], result[15]
			];
}

#pragma mark Standard Paths

+ (NSString *)cachesPath
{
	static dispatch_once_t onceToken;
	static NSString *cachedPath;
	
	dispatch_once(&onceToken, ^{
		cachedPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
	});
	
	return cachedPath;
}

+ (NSString *)documentsPath
{
	static dispatch_once_t onceToken;
	static NSString *cachedPath;
    
	dispatch_once(&onceToken, ^{
		cachedPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
	});
    
	return cachedPath;
}

#pragma mark Temporary Paths

+ (NSString *)temporaryPath
{
	static dispatch_once_t onceToken;
	static NSString *cachedPath;
	
	dispatch_once(&onceToken, ^{
		cachedPath = NSTemporaryDirectory();
	});
	
	return cachedPath;
}

+ (NSString *)pathForTemporaryFile
{
	CFUUIDRef newUniqueId = CFUUIDCreate(kCFAllocatorDefault);
	CFStringRef newUniqueIdString = CFUUIDCreateString(kCFAllocatorDefault, newUniqueId);
	NSString *tmpPath = [[NSString temporaryPath] stringByAppendingPathComponent:(__bridge NSString *)newUniqueIdString];
	CFRelease(newUniqueId);
	CFRelease(newUniqueIdString);
	
	return tmpPath;
}

#pragma mark Working with Paths

- (NSString *)pathByIncrementingSequenceNumber
{
	NSString *baseName = [self stringByDeletingPathExtension];
	NSString *extension = [self pathExtension];
	
	NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\(([0-9]+)\\)$" options:0 error:NULL];
	__block NSInteger sequenceNumber = 0;
	
	[regex enumerateMatchesInString:baseName options:0 range:NSMakeRange(0, [baseName length]) usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){
		
		NSRange range = [match rangeAtIndex:1]; // first capture group
		NSString *substring= [self substringWithRange:range];
		
		sequenceNumber = [substring integerValue];
		*stop = YES;
	}];
	
	NSString *nakedName = [baseName pathByDeletingSequenceNumber];
	
	if ([extension isEqualToString:@""])
	{
		return [nakedName stringByAppendingFormat:@"(%d)", (int)sequenceNumber+1];
	}
	
	return [[nakedName stringByAppendingFormat:@"(%d)", (int)sequenceNumber+1] stringByAppendingPathExtension:extension];
}

- (NSString *)pathByDeletingSequenceNumber
{
	NSString *baseName = [self stringByDeletingPathExtension];
	
	NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\([0-9]+\\)$" options:0 error:NULL];
	__block NSRange range = NSMakeRange(NSNotFound, 0);
	
	[regex enumerateMatchesInString:baseName options:0 range:NSMakeRange(0, [baseName length]) usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop) {
		
		range = [match range];
		
		*stop = YES;
	}];
	
	if (range.location != NSNotFound)
	{
		return [self stringByReplacingCharactersInRange:range withString:@""];
	}
	
	return self;
}

- (NSString *)stringByURLEncoding
{
	return (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL,  (__bridge CFStringRef)self,  NULL,  (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8));
}

+ (NSString *)MIMETypeForFileExtension:(NSString *)extension
{
	CFStringRef typeForExt = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,(__bridge CFStringRef)extension , NULL);
	NSString *result = (__bridge  NSString *)UTTypeCopyPreferredTagWithClass(typeForExt, kUTTagClassMIMEType);
	CFRelease(typeForExt);
	if (!result)
	{
		return @"application/octet-stream";
	}
	
	return result;
}

+ (NSString *)fileTypeDescriptionForFileExtension:(NSString *)extension
{
	CFStringRef typeForExt = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,(__bridge CFStringRef)extension , NULL);
	NSString *result = (__bridge  NSString *)UTTypeCopyDescription(typeForExt);
	CFRelease(typeForExt);
	return result;
}

+ (NSString *)universalTypeIdentifierForFileExtension:(NSString *)extension
{
	return (__bridge  NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,(__bridge CFStringRef)extension , NULL);
}

+ (NSString *)fileExtensionForUniversalTypeIdentifier:(NSString *)UTI
{
	return (__bridge  NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)(UTI), kUTTagClassFilenameExtension);
}

- (BOOL)conformsToUniversalTypeIdentifier:(NSString *)conformingUTI
{
	return UTTypeConformsTo((__bridge CFStringRef)(self), (__bridge CFStringRef)conformingUTI);
}

- (BOOL)isMovieFileName
{
	NSString *extension = [self pathExtension];
    
    // without extension we cannot know
    if (![extension length])
    {
        return NO;
    }
    
	NSString *uti = [NSString universalTypeIdentifierForFileExtension:extension];
    
	return [uti conformsToUniversalTypeIdentifier:@"public.movie"];
}

- (BOOL)isAudioFileName
{
	NSString *extension = [self pathExtension];
    
    // without extension we cannot know
    if (![extension length])
    {
        return NO;
    }
    
	NSString *uti = [NSString universalTypeIdentifierForFileExtension:extension];
	
	return [uti conformsToUniversalTypeIdentifier:@"public.audio"];
}

- (BOOL)isImageFileName
{
	NSString *extension = [self pathExtension];
    
    // without extension we cannot know
    if (![extension length])
    {
        return NO;
    }
    
	NSString *uti = [NSString universalTypeIdentifierForFileExtension:extension];
	
	return [uti conformsToUniversalTypeIdentifier:@"public.image"];
}

- (BOOL)isHTMLFileName
{
	NSString *extension = [self pathExtension];
    
    // without extension we cannot know
    if (![extension length])
    {
        return NO;
    }
    
	NSString *uti = [NSString universalTypeIdentifierForFileExtension:extension];
	
	return [uti conformsToUniversalTypeIdentifier:@"public.html"];
}


@end
